<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.1">
    <title>仪表盘</title>
    <script src="konva.min.js"></script>
    <style>
        body { 
            margin: 0; 
            padding: 0; 
            background:#000;
        }
        canvas { 
            display: block;
        }
		.canvas-container {
            width: 100vw;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            margin: 0;
            padding: 0;
        }
    </style>
</head>
<body>
	<div class="canvas-container">
		<div id="container"></div>
	</div>
    <script>
		//配置
		const width = 1200;
        const height = 800;
		const fontFamily = 'Oxanium';
		const fps = 60;
		const tireWearDeadLine = 0.6; //轮胎磨损预估上限

		const speedLight = [
			'#FFFFFF',
			'#FFFFFF',
			'#FFFFFF',
			'#FFFFFF',
			'#FFFFFF',
			'#00FF00',
			'#00FF00',
			'#00FF00',
			'#FF0000',
			'#FF0000'
		]; //转速灯

		const carLevel = [
			[999,'X','#405b42'],
			[901,'P','#0d8640'],
			[801,'R','#025bb6'],
			[701,'S','#613bbf'],
			[601,'A','#992800'],
			[501,'B','#c54e00'],
			[401,'C','#bb7a00'],
			[301,'D','#127f85'],
			[1,'E','#c7368e']
		]

		const tempLevel = [
			[0,'#3333FF'],
			[60,'#9999FF'],
			[75,'#FFFFFF'],
			[115,'#FFEE00'],
			[125,'#FF6600'],
			[135,'#FF0000']
		]

		const wearLevel = [
			[ 0 , '#FFFFFF' ],
			[ 0.50 , '#FFEE00' ],
			[ 0.60 , '#FF6600' ],
			[ 0.80 , '#FF0000' ]
		]


		//运行时数据
		let isRaceOn = false;
		let isFlash = false;
		const flashInterval = 100;

		const stage = new Konva.Stage({
            container: 'container',
            width: width,
            height: height
        });


		const ws = new WebSocket('ws://192.168.1.3:8080');

        const baseLayer = new Konva.Layer(); //非更新层
		const layer = new Konva.Layer(); //更新层
        stage.add(baseLayer);
		stage.add(layer);

       // 绘制背景
        const bg = new Konva.Rect({
            x: 0,
            y: 0,
            width: width,
            height: height,
            fill: '#000000'
        });
		baseLayer.add(bg);

		//绘制区块
		const blocks = [
			{
				name:'WEAR PER LAP',
				x:0,
				y:0,
				width:200,
				height:200,
				contents:data=>{
					let text = centerText({
						text: (()=>{
							let wearPerLap = getWearPerLap(data);
							return wearPerLap > 0 ? ( wearPerLap * 100 ).toFixed(1) : '-';
						})(),
						x: 0,
						y: 50,
						w: 200,
						h: 100,
						fontSize: 80,
					});
					let label = centerText({
						text: `%`,
						x: 0,
						y: 150,
						w: 200,
						h: 30,
						fontSize: 30,
						fill: '#666'
					});
					return [text,label];
				}
			},
			{
				name:'REMAIN TO '+( tireWearDeadLine * 100 ).toFixed(0)+'%',
				x:200,
				y:0,
				width:200,
				height:200,
				contents:data=>{
					let text = centerText({
						text: (()=>{
							let remain = '-';
							if( lapDistance > 0 && pitLaps.wear ){
								let wearPerLap = getWearPerLap(data);
								if( wearPerLap > 0 ){
									remain = (tireWearDeadLine - data.TireWear ) / wearPerLap;
									remain = remain > 99 ? '99+' : remain.toFixed(1);
								}
							}
							return remain;
						})(),
						x: 200,
						y: 50,
						w: 200,
						h: 100,
						fontSize: 80,
					});
					let label = centerText({
						text: `LAPS`,
						x: 200,
						y: 150,
						w: 200,
						h: 30,
						fontSize: 30,
						fill: '#666'
					});
					return [text,label];
				}
			},
			{
				name:'FUEL PER LAP',
				x:400,
				y:0,
				width:200,
				height:200,
				contents:data=>{
					let text = centerText({
						text:(()=>{
							let fuelPerLap = getFuelPerLap(data);
							return fuelPerLap > 0 ? ( fuelPerLap * 100 ).toFixed(1) : '-';
						})(),
						x: 400,
						y: 50,
						w: 200,
						h: 100,
						fontSize: 80,
					});
					let label = centerText({
						text: `%`,
						x: 400,
						y: 150,
						w: 200,
						h: 30,
						fontSize: 30,
						fill: '#666'
					});
					return [text,label];
				}
			},
			{
				name:'FUEL REMAIN',
				x:600,
				y:0,
				width:200,
				height:200,
				contents:data=>{
					let text = centerText({
						text: (()=>{
							let fuelRemain = '-';
							if( lapDistance > 0 && pitLaps.fuel ){
								let fuelPerLap = getFuelPerLap(data);
								if( fuelPerLap > 0 ){
									fuelRemain = data.Fuel / fuelPerLap;
									fuelRemain = fuelRemain > 99 ? '99+' : fuelRemain.toFixed(1);
								}
							}
							return fuelRemain;
						})(),
						x: 600,
						y: 50,
						w: 200,
						h: 100,
						fontSize: 80,
					});
					let label = centerText({
						text: `LAPS`,
						x: 600,
						y: 150,
						w: 200,
						h: 30,
						fontSize: 30,
						fill: '#666'
					});
					return [text,label];
				}
			},
			{
				name:'POSITION',
				x:800,
				y:0,
				width:200,
				height:100,
				contents:(data,block)=>{
					return [
						centerText({
							text: data.RacePosition || '-',
							x: 800,
							y: block.y+30,
							w: 200,
							h: 60,
							fontSize: 60,
						})
					]
				}
			},
			{
				name:'CURRENT LAP',
				x:1000,
				y:0,
				width:200,
				height:100,
				contents:(data,block)=>{
					return [
						centerText({
							text: data.IsRaceOn? data.LapNumber : '-',
							x: 1000,
							y: block.y+30,
							w: 200,
							h: 60,
							fontSize: 60,
						})
					]
				}
			},
			{
				name:'TEMPERATURE',
				x:0,
				y:200,
				width:400,
				height:200,
				contents:data=>{
					return [
						centerText({
							text: data.TireTempFrontLeft ? ( Math.round((data.TireTempFrontLeft - 32) / 1.8) + '°C' ) : '-',
							x: 0,
							y: 240,
							w: 200,
							h: 80,
							fontSize: 60,
							fill:getTempColor(data.TireTempFrontLeft)
						}),
						centerText({
							text: data.TireTempFrontRight ? ( Math.round((data.TireTempFrontRight - 32) / 1.8) + '°C' ) : '-',
							x: 200,
							y: 240,
							w: 200,
							h: 80,
							fontSize: 60,
							fill:getTempColor(data.TireTempFrontRight)
						}),
						centerText({
							text: data.TireTempRearLeft  ? ( Math.round((data.TireTempRearLeft - 32) / 1.8) + '°C' ): '-',
							x: 0,
							y: 320,
							w: 200,
							h: 80,
							fontSize: 60,
							fill:getTempColor(data.TireTempRearLeft)
						}),
						centerText({
							text: data.TireTempRearRight  ? ( Math.round((data.TireTempRearRight - 32) / 1.8) + '°C' ) : '-',
							x: 200,
							y: 320,
							w: 200,
							h: 80,
							fontSize: 60,
							fill:getTempColor(data.TireTempRearRight)
						})
					]
				}
			},
			{ 
				name:'TIRE WEAR',
				x:0,
				y:400,
				width:400,
				height:200,
				contents:data=>{
					return [
						centerText({
							text: data.TireWearFrontLeft ? ( (data.TireWearFrontLeft * 100).toFixed(1) + '%' ) : '-',
							x: 0,
							y: 440,
							w: 200,
							h: 80,
							fontSize: 60,
							fill:getWearColor(data.TireWearFrontLeft)
						}),
						centerText({
							text: data.TireWearFrontRight ? ( (data.TireWearFrontRight * 100).toFixed(1) + '%' ) : '-',
							x: 200,
							y: 440,
							w: 200,
							h: 80,
							fontSize: 60,
							fill:getWearColor(data.TireWearFrontRight)
						}),
						centerText({
							text: data.TireWearRearLeft ? ( (data.TireWearRearLeft * 100).toFixed(1) + '%' ) : '-',
							x: 0,
							y: 520,
							w: 200,
							h: 80,
							fontSize: 60,
							fill:getWearColor(data.TireWearRearLeft)
						}),
						centerText({
							text: data.TireWearRearRight ? ( (data.TireWearRearRight * 100).toFixed(1) + '%' ) : '-',
							x: 200,
							y: 520,
							w: 200,
							h: 80,
							fontSize: 60,
							fill:getWearColor(data.TireWearRearRight)
						})
					]
				}
			},
			{
				//main block
				x:400,
				y:200,
				width:400,
				height:400,
				contents:(data,block)=>{
					let contents = [];
					//转速
					speedLight.forEach((color, i) => {
						contents.push( new Konva.Circle({
							x: block.x + i * 36 + 38,
							y: block.y + 50,
							radius: 15,
							fill:(data=>{
								const limit = data.EngineMaxRpm / speedLight.length * i;
								const rpm = data.CurrentEngineRpm;
								let color = '#333333';
								if( rpm && rpm >= limit){
									color = speedLight[i];
								}
								return color;
							})(data)
						}) )
					});
					//档位
					contents.push( centerText({
						x: block.x,
						y: block.y + 40,
						w:400,
						h:300,
						text:  data.IsRaceOn && data.Gear <= 10 ? (data.Gear <= 0 ? 'R' : data.Gear) : 'N',
						fontSize: 240,
						fill:  data.CurrentEngineRpm && data.CurrentEngineRpm >= data.EngineMaxRpm * 0.8 && isFlash ? '#FF0000' : '#FFFFFF'
					}) );
					//速度
					contents.push( centerText({
						x: block.x,
						y: block.y + 170,
						w:400,
						h:300,
						text: Math.floor(data.Speed * 3.6) || '0',
						fontSize: 80,
					}) );
					//KM/H
					contents.push( centerText({
						x: block.x,
						y: block.y + 220,
						w:400,
						h:300,
						text: 'KM/H',
						fill:'#666',
						fontSize: 24,
					}) );
					return contents;
				}
			},
			{   //infomation block
				name:'CAR',
				x:0,
				y:600,
				width:200,
				height:200,
				contents:(data,block)=>{
					return [
						//驱动类型 "0 = FWD, 1 = RWD, 2 = AWD."
						new Konva.Text({
							x:block.x + 10,
							y:block.y + 40,
							fontFamily,
							fontSize:40,
							fill:'#dddddd',
							text:(type=>{
								let text = [
									'FWD',
									'RWD',
									'AWD'
								][type] 
								return text || '-';
							})(data.DrivetrainType) ,
						}),
						//车辆等级外框
						new Konva.Rect({
							x:block.x + 10,
							y:block.y + 90,
							width:180,
							height:70,
							cornerRadius:10,
							fill:(index=>{
								let color;
								carLevel.some( item=>{
									if( index >= item[0] ){
										color = item[2];
										return true;
									}
								} )
								return color;
							})(data.CarPerformanceIndex)
						}),
						//车辆等级文字
						new Konva.Text({
							x:block.x + 22,
							y:block.y + 100,
							fontFamily,
							fontSize:60,
							fill:'#ffffff',
							text:(index=>{
								let text;
								carLevel.some( item=>{
									if( index >= item[0] ){
										text = item[1];
										return true;
									}
								} )
								return text;
							})(data.CarPerformanceIndex) ,
						}),
						//车辆BOP外框
						new Konva.Rect({
							x:block.x + 70,
							y:block.y + 95,
							width:115,
							height:60,
							cornerRadius:5,
							fill:'#dddddd',
						}),
						//车辆BOP文字
						centerText({
							x:block.x + 70,
							y:block.y + 100,
							w:115,
							h:60,
							text: data.CarPerformanceIndex,
							fill:'#000000',
							fontSize: 60,
						}),
					];
				}
			},
			{
				name:'FUEL',
				x:200,
				y:600,
				width:200,
				height:200,
				contents:data=>{
					let text = centerText({
						text: data.Fuel ? ( (data.Fuel * 100).toFixed(1)) : '-',
						x: 200,
						y: 650,
						w: 200,
						h: 100,
						fontSize: 80,
					});
					let label = centerText({
						text: `%`,
						x: 200,
						y: 750,
						w: 200,
						h: 30,
						fontSize: 30,
						fill: '#666'
					});
					return [text,label];
				}
			},
			{
				//operation block
				x:400,
				y:600,
				width:400,
				height:200,
				contents:(data,block)=>{
					return [
						//方向底色
						new Konva.Arc({
							x:block.x + 120,
							y:block.y + 100,
							innerRadius: 40,
							outerRadius: 70,
							angle: 270,
							rotation:135,
							fill: '#dddddd',
							strokeWidth: 10,
						}),
						//方向颜色
						new Konva.Arc({
							x:block.x + 120,
							y:block.y + 100,
							innerRadius: 40,
							outerRadius: 70,
							angle: 30,
							rotation:((steer)=>{
								return - 105 +  (135-15) * steer/127;
							})( data.Steer || 0 ),
							fill: '#3333DD',
							strokeWidth: 10,
						}),
						//方向文字
						centerText({
							x:block.x + 70,
							y:block.y + 44,
							w:100,
							h:100,
							text: (()=>{
								return Math.round( Math.abs( data.Steer / 127 ) * 100 ) || '0'
							})(),
							fontSize: 36,
							fill: '#dddddd',
						}),
						//百分号
						centerText({
							x:block.x + 70,
							y:block.y + 70,
							w:100,
							h:100,
							text: '%',
							fontSize: 20,
							fill: '#dddddd',
						}),
						//刹车踏板背景色
						new Konva.Rect({
							x:block.x + 240,
							y:block.y + 30,
							width:30,
							height:120,
							fill: '#4F4F4F',
						}),
						//刹车踏板颜色
						new Konva.Rect({
							x:block.x + 240,
							y:(value=>{
								return block.y + 30 + 120 - (value / 255 * 120);
							})(data.Brake),
							width:30,
							height:(value=>{
								return value / 255 * 120;
							})(data.Brake),
							fill:'#ff0000'
						}),
						//刹车文字
						centerText({
							x:block.x + 240,
							y:block.y + 110,
							w:30,
							h:135,
							text: Math.round( data.Brake / 255 * 100 ) || '0',
							fontSize: 36,
							fill: '#ffffff',
						}),
						//油门踏板背景色
						new Konva.Rect({
							x:block.x + 300,
							y:block.y + 30,
							width:30,
							height:120,
							fill: '#4F4F4F',
						}),
						//油门踏板颜色
						new Konva.Rect({
							x:block.x + 300,
							y:(value=>{
								return block.y + 30 + 120 - (value / 255 * 120);
							})(data.Accel),
							width:30,
							height:(value=>{
								return value / 255 * 120;
							})(data.Accel),
							fill:'#00ff00'
						}),
						//油门文字
						centerText({
							x:block.x + 300,
							y:block.y + 110,
							w:30,
							h:135,
							text: Math.round( data.Accel / 255 * 100 ) || '0',
							fontSize: 36,
							fill: '#ffffff',
						}),
					]
				}
			},
			{
				name:'LAP TIME',
				x:800,
				y:100,
				width:400,
				height:100,
				contents:(data,block)=>{
					return [
						centerText({
							x:block.x,
							y:block.y+30,
							w:400,
							h:60,
							fontSize:60,
							text: data.CurrentLap ? formatTime(data.CurrentLap) : '-',
						})
					]
				}
			},
			{
				name:'BEST TIME',
				x:800,
				y:200,
				width:400,
				height:100,
				contents:(data,block)=>{
					return [
						centerText({
							x:block.x,
							y:block.y+30,
							w:400,
							h:60,
							fontSize:60,
							text: data.BestLap && data.BestLap ? formatTime(data.BestLap) : '-',
						})
					]
				}
			},
			{
				name:'RECENT LAPS',
				x:800,
				y:300,
				width:400,
				height:400,
				contents:(data,block)=>{
					let contents = [];
					let count = 0;
					for(let i = data.LapNumber - 1 ; i > 0 && count < 5; i--){
						if( recentLaps[i] ){
							//时间
							contents.push( centerText({
								x:block.x + 30,
								y:block.y+50+count*75,
								w:300,
								h:30,
								fontSize:50,
								text: formatTime(recentLaps[i].LapTime),
							}) );
							//序号背景
							contents.push( new Konva.Rect({
								x:block.x + 15 ,
								y:block.y+47+count*75,
								width:40,
								height:30,
								fill: '#dddddd',
								cornerRadius:5,
							}) );
							//序号文字
							contents.push( centerText({
								x:block.x + 15,
								y:block.y+50+count*75,
								w:40,
								h:30,
								fontSize:30,
								text: recentLaps[i].LapNumber,
								fill:'#1e1e1e'
							}) );
							//间隔时间
							if( recentLaps[i].LapTime && recentLaps[i-1] && recentLaps[i-1].LapTime ){
								let diffTime = recentLaps[i].LapTime - recentLaps[i-1].LapTime;
								contents.push( new Konva.Text({
									x:block.x + 306,
									y:block.y+63+count*75,
									text: formatTime2( diffTime ) + '"',
									fontSize: 20,
									fill: diffTime < 0 ? '#00ff00' : '#ff0000',
									fontFamily
								}) )
							}
							count++;
						}
						
					}
					return contents;
				}
			},
			{
				name:'RACE TIME',
				x:800,
				y:700,
				width:400,
				height:100,
				contents:(data,block)=>{
					return [
						centerText({
							x:block.x,
							y:block.y+30,
							w:400,
							h:60,
							fontSize:60,
							text: data.CurrentRaceTime ? formatTime(data.CurrentRaceTime) : '-',
						})
					]
				}
			}
			
		];
		blocks.forEach(block => {
			const rect = new Konva.Rect({
				x: block.x,
				y: block.y,
				width: block.width,
				height: block.height,
				fill: '#1e1e1e',
				stroke: '#000000',
				strokeWidth: 5,
				cornerRadius:10,
			});
			baseLayer.add(rect);
			const text = new Konva.Text({
				x: block.x + 10,
				y: block.y + 10,
				text: block.name,
				fontSize: 20,
				fontFamily,
				fill: '#454545'
			});
			baseLayer.add(text);
		});
		

		//通用方法
		//居中文字
		const centerText = options=>{
			options = options || {};
			options = Object.assign({
				text: '',
				x: 0,
				y: 0,
				fontSize: 20,
				fontFamily,
				fill: '#dddddd'
			}, options);
			const text = new Konva.Text(options);
			const width = text.getTextWidth();
			const height = text.height();
			text.setX(options.x + (options.w - width) / 2);
			text.setY(options.y + (options.h - height) / 2);
			return text;
		}

		//格式化时间，转为 分:秒.毫秒 格式
		const formatTime = time => {
			const minute = Math.floor(time / 60);
			const second = Math.floor(time % 60);
			const millisecond = Math.round((time - Math.floor(time)) * 1000);
			return `${minute < 10 ? ('0'+minute) : minute}:${('0'+second).slice(-2)}.${('000'+millisecond).slice(-3)}`;
		};

		//格式化时间，转换为 +/-秒.毫秒 格式，长度固定为6
		const formatTime2 = time => {
			const sige = time < 0 ? '' : '+';
			let digit = 3;
			if( Math.abs(time)  >= 100 ){
				digit = 2;
			}
			if( Math.abs(time)  >= 1000 ){
				digit = 1;
			}
			if( Math.abs(time)  >= 10000 ){
				digit = 0;
			}
			return sige + ( time.toFixed(digit) );
		};

		//获取轮胎温度颜色
		const getTempColor = temperature=>{
			let color = '#dddddd';
			temperature =(temperature - 32) / 1.8
			tempLevel.forEach( item=>{
				if( temperature > item[0] ){
					color = item[1];
				}
			} )
			return color;
		}
		//获取轮胎损耗颜色
		const getWearColor = tireWear=>{
			let color = '#dddddd';
			wearLevel.forEach( item=>{
				if( tireWear > item[0] ){
					color = item[1];
				}
			} )
			return color;
		}
		//获取最高轮胎损耗
		const getMaxTireWear = data=>{
			return Math.max( data.TireWearFrontLeft || 0, data.TireWearFrontRight || 0, data.TireWearRearLeft || 0, data.TireWearRearRight || 0 )
		}
		//获取每圈轮胎损耗
		const getWearPerLap = data=>{
			if( lapDistance > 0 && pitLaps.wear && data ){
				let distance = data.DistanceTraveled - pitLaps.wear.DistanceTraveled;
				let wear = data.TireWear - pitLaps.wear.TireWear;
				let lapCount = distance / lapDistance;
				let perLap = wear / lapCount;
				return perLap > 1 ? 1 : perLap;
			}
			return false;
		}
		//获取每圈油耗
		const getFuelPerLap = data=>{
			if( lapDistance > 0 && pitLaps.fuel && data ){
				let distance = data.DistanceTraveled - pitLaps.fuel.DistanceTraveled;
				let fuel = pitLaps.fuel.Fuel - data.Fuel;
				let lapCount = distance / lapDistance;
				let perLap = fuel / lapCount;
				return perLap > 1 ? 1 : perLap;
			}
			return false;
		}
        
		//监听数据变化
		let time = 0;
		let recentLaps = {};
		let pitLaps = {};
		let lastData = false;
		let lapDistance = 0;
		ws.onmessage = function(event) {
			let nowTime = new Date().getTime();
			const data = JSON.parse(event.data);
			if( nowTime - time < 1000 / fps ){
				if( data.IsRaceOn ){
					lastData = data;
				}
				return;
			}
			console.log(data)
			data.LapNumber = data.LapNumber  === undefined ? -1 : data.LapNumber;
			if( data.IsRaceOn && data.CurrentLap <= 0 ){
				data.LapNumber = -1;
			}
			data.LapNumber = data.LapNumber + 1;
			lapDistance = data.DistanceTraveled < 0 && data.DistanceTraveled < 0 - lapDistance ?  Math.abs( data.DistanceTraveled ) : lapDistance;
			if( data.LapNumber === 0 ){//起跑前
				if( data.DistanceTraveled ){
					let initData = {
						DistanceTraveled:0,
						LapNumber : 0,
						TireWear:0,
					}
					recentLaps = {};
					pitLaps = {
						'fuel':Object.assign({},data,initData),
						'wear':Object.assign({},data,initData),
					};
				}
				newFuel = data.Fuel;
				data.DistanceTraveled = 0;
			}
			if( nowTime - time > flashInterval ){
				isFlash = !isFlash;
				time = nowTime;
			}
			if( data.IsRaceOn ){
				data.TireWear = getMaxTireWear(data);
				recentLaps[ data.LapNumber ] = data;
				if( recentLaps[ data.LapNumber - 1 ] && data.LastLap ){
					recentLaps[ data.LapNumber - 1 ].LapTime = data.LastLap;
				}
				if( lastData && data.Fuel > lastData.Fuel ){//加了油
					console.log('加了油')
					pitLaps.fuel = data;
				}
				if( lastData && data.TireWear < lastData.TireWear ){//换了胎
					console.log('换了胎')
					pitLaps.wear = data;
				}
				lastData = data;
			}
			
			//更新区块数据
			loop( data );
		}

		const loop=data=>{
			//更新区块数据
			layer.destroyChildren();
			blocks.forEach(block => {
				if(block.contents){
					const contents = block.contents(data,block);
					contents.forEach(content => {
						layer.add(content);
					});
				}
			});
			layer.draw();
		}
		const font = new FontFace('Oxanium', 'url(./fonts/oxanium.ttf)');
		font.load().then(() => {
			document.fonts.add(font);
			baseLayer.draw(); 
			loop({})
		});

		
		//窗口缩放
		function resizeCanvas() {
            // 获取容器尺寸
			const container = document.querySelector('.canvas-container');
            const containerWidth = container.clientWidth;
            const containerHeight = container.clientHeight;
            
            // 计算缩放比例
            const scale = Math.min(
                containerWidth / width,
                containerHeight / height
            );
            const canvas = document.getElementsByTagName('canvas');
			const box = document.getElementsByClassName('konvajs-content');
            // 应用缩放尺寸到CSS（保持比例）
            box[0].style.width = canvas[0].style.width = canvas[1].style.width = `${width * scale}px`;
            box[0].style.height =canvas[0].style.height =canvas[1].style.height  = `${height * scale}px`;
        }

        // 初始调整
        resizeCanvas();
        // 监听窗口变化
        window.addEventListener('resize', resizeCanvas);
    </script>
</body>
</html>
